package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.utils.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName WmNewsServiceImpl
 * @Description TODO
 * @Author AWei
 * @DATE 2021/4/15 17:35
 * @Version 1.0
 */
@Slf4j
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    private String webSite;

    @Resource
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Resource
    private WmMaterialMapper wmMaterialMapper;

    @Resource
    private KafkaTemplate<String, String> kafkaTemplate;

    @Resource
    private WmNewsMapper wmNewsMapper;

    @Resource
    private WmUserMapper wmUserMapper;

    /**
     * @param wmNewsPageReqDto 请求携带数据封装对象
     * @return 查询结果
     * @Description 分页带条件查询自媒体文章列表
     */
    @Override
    public ResponseResult findAllNewsByAnyCondition(WmNewsPageReqDto wmNewsPageReqDto) {
        //1. 校验参数：分页数据不能为空，为空则设为默认值
        wmNewsPageReqDto.checkParam();
        //2. 判断是否用户登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user)) {
            CustomException.custom(AppHttpCodeEnum.NEED_LOGIN);
        }
        //3. 封装查询条件
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        //模糊查询
        if (StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())) {
            wrapper.like(WmNews::getTitle, wmNewsPageReqDto.getKeyword());
        }
        //所属频道id
        if (!ObjectUtils.isEmpty(wmNewsPageReqDto.getChannelId())) {
            wrapper.eq(WmNews::getChannelId, wmNewsPageReqDto.getChannelId());
        }
        // 文章状态
        if (!ObjectUtils.isEmpty(wmNewsPageReqDto.getStatus())) {
            wrapper.eq(WmNews::getStatus, wmNewsPageReqDto.getStatus());
        }
        // 发布时间
        if (!ObjectUtils.isEmpty(wmNewsPageReqDto.getBeginPubDate()) && !ObjectUtils.isEmpty(wmNewsPageReqDto.getEndPubDate())) {
            wrapper.between(WmNews::getPublishTime, wmNewsPageReqDto.getBeginPubDate(), wmNewsPageReqDto.getBeginPubDate());
        }
        wrapper.eq(WmNews::getUserId, user.getId());
        wrapper.orderByDesc(WmNews::getCreatedTime);
        //4. 执行分页查询并返回结果
        IPage<WmNews> page = page(new Page<>(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize()), wrapper);
        PageResponseResult result = PageResponseResult.page(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), page.getTotal(), page.getRecords());
        result.setHost(webSite);
        return result;
    }

    @Override
    @Transactional
    public ResponseResult submitNews(WmNewsDto wmNewsDto, Short submitStatus) {
        //1. 校验参数：文章内容不能为空
        if (ObjectUtils.isEmpty(wmNewsDto.getContent())) {
            CustomException.custom(AppHttpCodeEnum.PARAM_REQUIRE, "文章内容不能为空！");
        }
        //判断是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user)) {
            CustomException.custom(AppHttpCodeEnum.NEED_LOGIN);
        }
        //复制WmNewsDto属性到WmNews中
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, wmNews);
        wmNews.setUserId(user.getId());
        //将自动生成封面属性保存为null
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(wmNewsDto.getType())) {
            wmNews.setType(null);
        }
        //将封面图片路径转成字符串，保存到文章对象中
        List<String> images = wmNewsDto.getImages();
        if (!ObjectUtils.isEmpty(images)) {
            wmNews.setImages(
                    images.stream()
                            .map(image -> image.replace(webSite, ""))
                            .collect(Collectors.joining(","))
            );
        }
        //2. 保存或修改文章
        saveOrUpdateWmNews(wmNews);
        //3. 判断若为提交操作维护中间表信息，否则直接返回保存结果
        if (WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(submitStatus)) {
            //将内容图片地址提取
            List<String> imageList = parseContentImages(wmNewsDto.getContent());
            if (!ObjectUtils.isEmpty(imageList)) {
                //保存文章图片和素材关系
                saveContentAndMaterial(imageList, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);
            }
            //保存封面图片和素材关系(处理自动生成封面)
            saveCoverAndMaterial(imageList, wmNewsDto, wmNews);
            // 4 发消息给admin审核文章
            Map<String, Integer> map = new HashMap<>();
            map.put("wmNewsId", wmNews.getId());
            log.info("文章id开始发送 => {}", wmNews.getId());
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(map));
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult findWmNewsById(Integer id) {
        // 1. 检查参数
        if (ObjectUtils.isEmpty(id)) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据id查询文章信息
        WmNews wmNews = getById(id);
        if (ObjectUtils.isEmpty(wmNews)) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        // 3. 返回结果
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        //前端自动拼接
        responseResult.setHost(webSite);
        return responseResult;
    }

    @Override
    public ResponseResult delNews(Integer id) {
        // 1. 检查参数 判断是否登录
        if (ObjectUtils.isEmpty(id)) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user)) {
            CustomException.custom(AppHttpCodeEnum.NO_OPERATOR_AUTH, "请登录之后在操作");
        }
        // 2. 根据id查询文章
        WmNews wmNews = getOne(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId, id).eq(WmNews::getUserId, user.getId()));
        if (ObjectUtils.isEmpty(wmNews)) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "未查询到相关文章信息");
        }
        // 3. 如果文章状态为9(发布) 并且 上架状态: 1(上架) 不可以删除
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode()) && wmNews.getEnable() == 1) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "当前文章已发布，请先下架后再删除");
        }
        // 4. 删除文章与素材的关联关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, id));
        // 5. 删除文章信息
        removeById(id);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult downOrUp(WmNewsDto newsAuthDto) {
        // 1. 校验参数：id不能为空
        if (ObjectUtils.isEmpty(newsAuthDto.getId()) || ObjectUtils.isEmpty(newsAuthDto.getEnable())) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID);
        }
        if (!newsAuthDto.getEnable().equals(WemediaConstants.WM_NEWS_DOWN) && !newsAuthDto.getEnable().equals(WemediaConstants.WM_NEWS_UP)) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID, "文章上下架参数错误");
        }
        // 用户是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user)) {
            CustomException.custom(AppHttpCodeEnum.NO_OPERATOR_AUTH, "请先登录");
        }
        // 2. 根据id查询出文章数据
        WmNews wmNews = getOne(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId, newsAuthDto.getId()).eq(WmNews::getUserId, user.getId()));
        if (ObjectUtils.isEmpty(wmNews)) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "未查询到该文章");
        }
        // 3. 判断文章状态是否为9(发布)，发布状态的文章才可以上下架
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            CustomException.custom(AppHttpCodeEnum.DATA_ERROR, "只有发布状态的文章才能上下架");
        }
        // 4. 根据id修改文章enable
        update(Wrappers.<WmNews>lambdaUpdate().set(WmNews::getEnable, newsAuthDto.getEnable()).eq(WmNews::getId, newsAuthDto.getId()));
        //TODO 5. 同步到app端
        //6. 向admin管理端发送下架消息
        if (wmNews.getArticleId() != null) {
            Map<String, Object> msgMap = new HashMap<>();
            msgMap.put("enable", newsAuthDto.getEnable());
            msgMap.put("articleId", wmNews.getArticleId());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(msgMap));
            if(newsAuthDto.getEnable().intValue() == 0){
                kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_DELETE,JSON.toJSONString(msgMap));
            }else {
                kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSON.toJSONString(msgMap));
            }
        }
        return ResponseResult.okResult();
    }

    /**
     * @return 查询结果
     * @Description 查询需要发布的文章id列表
     */
    @Override
    public List<Integer> findRelease() {
        List<WmNews> releaseList = list(Wrappers.<WmNews>lambdaQuery()
                .in(WmNews::getStatus, 4, 8)
                .le(WmNews::getCreatedTime, new Date())
                .select(WmNews::getId));
        return releaseList.stream().map(WmNews::getId).collect(Collectors.toList());
    }

    /**
     * @param newsAuthDto 文章列表分页查询对象
     * @return 分页查询结果集
     * @Description 查询文章列表
     */
    @Override
    public ResponseResult findList(NewsAuthDto newsAuthDto) {
        //1.校验参数
        newsAuthDto.checkParam();
        //记录当前页
        int currentPage = newsAuthDto.getPage();
        //设置起始页
        newsAuthDto.setPage((newsAuthDto.getPage() - 1) * newsAuthDto.getSize());
        if (StringUtils.isNotBlank(newsAuthDto.getTitle())) {
            newsAuthDto.setTitle("%" + newsAuthDto.getTitle() + "%");
        }
        //2.分页查询文章列表
        List<WmNewsVo> wmNewsVoList = wmNewsMapper.findListAndPage(newsAuthDto);
        //统计查询文章条数
        Long count = wmNewsMapper.findListCount(newsAuthDto);
        //3.结果返回
        ResponseResult result = PageResponseResult.page(currentPage, newsAuthDto.getSize(), count, wmNewsVoList);
        return result;
    }

    /**
     * @param id 文章id
     * @return 查询结果
     * @Description 查询文章详情
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        //校验参数
        if (ObjectUtils.isEmpty(id)) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章信息
        WmNews wmNews = getById(id);
        if (ObjectUtils.isEmpty(wmNews)) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.查询作者信息
        WmUser wmUser = null;
        if (!ObjectUtils.isEmpty(wmNews.getUserId())) {
            wmUser = wmUserMapper.selectById(wmNews.getUserId());
        }

        //4.封装vo信息并返回
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews, wmNewsVo);
        if (!ObjectUtils.isEmpty(wmUser)) {
            wmNewsVo.setAuthorName(wmUser.getName());
        }
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
        responseResult.setHost(webSite);
        return responseResult;
    }

    /**
     * @param wmNewsAuthPass 人工审核状态
     * @param newsAuthDto    文章列表分页查询对象
     * @return 更改结果
     * @Description 更改文章状态
     */
    @Override
    public ResponseResult updateStatus(Short wmNewsAuthPass, NewsAuthDto newsAuthDto) {
        //1.参数检查
        if (ObjectUtils.isEmpty(newsAuthDto) || ObjectUtils.isEmpty(newsAuthDto.getId())) {
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章
        WmNews wmNews = getById(newsAuthDto.getId());
        if (ObjectUtils.isEmpty(wmNews)) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.修改文章状态
        wmNews.setStatus(wmNewsAuthPass);
        if (StringUtils.isNotBlank(newsAuthDto.getMsg())) {
            wmNews.setReason(newsAuthDto.getMsg());
        }
        updateById(wmNews);
        return ResponseResult.okResult();
    }


    /**
     * @param imageList 封面图片地址集合
     * @param wmNewsDto 文章发布前端数据封装对象
     * @param wmNews    自媒体端文章实体类对象
     * @Description 保存封面和素材表信息
     */
    private void saveCoverAndMaterial(List<String> imageList, WmNewsDto wmNewsDto, WmNews wmNews) {
        // 1. 获取请求中的文章封面图片列表
        List<String> images = wmNewsDto.getImages();
        // 2. 判断当前封面图片类型是否是  自动生成
        if (wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            if (imageList != null && !imageList.isEmpty()) {
                // 内容图片列表不为空
                int imgNum = imageList.size();
                // 单图类型
                if (imgNum <= 2) {
                    wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                    images = imageList.stream().limit(1).collect(Collectors.toList());
                } else { // 多图类型
                    wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                    images = imageList.stream().limit(3).collect(Collectors.toList());
                }
                wmNews.setImages(images.stream()
                        .map(url -> url.replace(webSite, ""))
                        .collect(Collectors.joining(",")));
            } else {
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            // 更新文章信息
            updateById(wmNews);
        }
        if (images != null && !images.isEmpty()) {
            // 去除url 前缀
            images = images.stream()
                    .map(url -> url.replace(webSite, ""))
                    .collect(Collectors.toList());
            // 保存封面图片  与  素材的关联关系
            saveContentAndMaterial(images, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
        }
    }

    private void saveContentAndMaterial(List<String> imageList, Integer id, Short type) {
        saveNewsAndMaterial(imageList, id, type);
    }

    /**
     * @param imageList 素材图片路径
     * @param newsId    文章ID
     * @param type      引用类型 0:内容引用 1:封面引用
     * @Description 保存图片和素材关系方法
     */
    private void saveNewsAndMaterial(List<String> imageList, Integer newsId, Short type) {
        // 1. 根据图片路径及登录用户id条件，查询相关图片ID列表
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery()
                        .in(WmMaterial::getUrl, imageList)
                        .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
        );
        if (ObjectUtils.isEmpty(wmMaterials) || wmMaterials.size() == 0) {
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "图片素材信息不存在！");
        }
        // 2. 判断所有素材是否全部存在
        List<Integer> materialIdList = new ArrayList<>();
        Map<String, Integer> imageUrlAndIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        for (String imageUrl : imageList) {
            if (imageUrlAndIdMap.get(imageUrl) == null) {
                CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "图片素材不存在！");
            }
            materialIdList.add(imageUrlAndIdMap.get(imageUrl));
        }
        // 3. 图片和素材关系
        wmNewsMaterialMapper.saveRelations(materialIdList, newsId, type);
    }

    /**
     * @param content 文章内容Jason字符串
     * @return 抽取结果集
     * @Description 抽取文章内容中所包含的所有图片路径
     */
    private List<String> parseContentImages(String content) {
        //1. 将json字符串转为：List<Map>
        List<Map> contentList = JSONArray.parseArray(content, Map.class);
        return contentList.stream().filter(this::isImage)
                .map(map -> (String) map.get("value"))
                .map(image -> image.replace(webSite, ""))
                .collect(Collectors.toList());
    }

    /**
     * @param map content map集合
     * @return 判断结果
     * @Description 判断map元素类型是否为image
     */
    private boolean isImage(Map map) {
        return WemediaConstants.WM_NEWS_TYPE_IMAGE.equals(map.get("type"));
    }

    /**
     * @param wmNews
     * @Description 保存或修改文章
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        if (ObjectUtils.isEmpty(wmNews.getId())) {
            // 添加文章
            wmNews.setCreatedTime(new Date());
            wmNews.setEnable((short) 1);
            wmNews.setSubmitedTime(new Date());
            save(wmNews);
        } else {
            // 删除素材和文章的关联关系
            wmNewsMaterialMapper.delete(
                    Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId())
            );
            // 修改文章
            updateById(wmNews);
        }
    }


}
